/**
 * webpack common
 */

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const WebpackAssetsManifest = require('webpack-assets-manifest');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const WebpackProgressBar = require('progress-bar-webpack-plugin');
const FastUglifyJsPlugin = require('./scripts/fast-uglifyjs-plugin');

const DllPlugin = webpack.DllPlugin;
const DllReferencePlugin = webpack.DllReferencePlugin;

const pkgs = require('./package.json');
const createPaths = require('./webpack.paths');

const IS_PROD = process.env.NODE_ENV === 'production';
const NODE_ENV = IS_PROD ? 'production' : 'development';
const NPM_ROOT = path.resolve(__dirname, 'node_modules');

const BASE64_LIMIT = 10000;

module.exports = function(extendConfig, {
    appPath,
    cleanDist = false,
    addDLLDefine = false,
    addDLLReference = false,
    resetManifest = false,
    extractCSS = IS_PROD,
    cssLoaders = 'css!postcss!less?relativeUrls=true',
    getPostCSSConfig = () => {
        return () => {
            const plugins = [
                require('postcss-inline-svg')({
                    removeFill: false
                })
            ];

            if(IS_PROD) {
                plugins.push(
                    require('autoprefixer')({
                        browsers: ['>5%', 'last 2 versions']
                    })
                );
            }

            return plugins;
        };
    },
    definedVars = null
}) {
    const appName = path.basename(appPath);
    const PATHS = createPaths({ appName });

    const output = {
        library: `lib_${appName}_[name]`,
        jsonpFunction: `webpackJsonp_${appName}`,
        filename: IS_PROD ? '[name].[chunkhash:6].js' : '[name].js',
        chunkFilename: 'chunk-[name].[chunkhash:6].js',
        publicPath: PATHS.PUBLIC,
        path: PATHS.DIST
    };

    const plugins = [
        // Clean
        cleanDist ? new CleanWebpackPlugin([PATHS.DIST], {
            verbose: false
        }) : () => {},

        // DLL Support
        addDLLDefine ? new DllPlugin({
            path: PATHS.VENDOR_DLL,
            name: output.library,
            context: PATHS.DIST
        }) : () => {},
        addDLLReference ? new DllReferencePlugin({
            manifest: fs.existsSync(PATHS.VENDOR_DLL) ? require(PATHS.VENDOR_DLL) : {},
            context: PATHS.DIST
        }) : () => {},

        // Define
        new webpack.DefinePlugin(Object.assign({
            'process.env.VERSION': JSON.stringify(pkgs.version),
            'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
            __VERSION__: JSON.stringify(pkgs.version),
            __CDN_URL__: JSON.stringify(PATHS.PUBLIC),
            __DEV__: IS_PROD ? '' : 'true'
        }, definedVars)),

        new ExtractTextPlugin(`[name]${IS_PROD ? '.[contenthash:6]' : ''}.css`, {
            disable: !extractCSS,
            allChunks: true
        }),

        IS_PROD ? new FastUglifyJsPlugin({
            compress: {
                warnings: false
            },
            cacheFolder: path.join(__dirname, '.uglify-js-cache')
        }) : () => {},

        new webpack.BannerPlugin(`(c)${new Date().getFullYear()} ${pkgs.name}-${appName}`),
        new WebpackAssetsManifest({
            publicPath: '',
            output: PATHS.MANIFEST,
            merge: !resetManifest,
            processAssets() {
                // For nameless chunks, just map itself.
                this.stats.assets.forEach(asset => {
                    const {name, chunks, chunkNames} = asset;

                    if(
                        chunks &&
                        chunkNames &&
                        chunks.length &&
                        !chunkNames.length
                    ) {
                        this.set(name, name);
                    }
                });
            }
        })
    ];

    const config = Object.assign({
        bail: true,
        context: appPath,
        devtool: IS_PROD ? false : 'source-map',
        externals: {},
        entry: {},
        output,

        resolve: {
            extensions: ['', '.js', '.vue'],
            alias: {
                jquery: path.join(NPM_ROOT, 'jquery/dist/jquery.js'),
                vue: path.join(NPM_ROOT, 'vue/dist/vue.js')
            }
        },
        module: {
            preLoaders: [
                {
                    test: /\.vue$/,
                    loader: 'eslint',
                    exclude: /node_modules/
                }, {
                    test: /\.js$/,
                    loader: 'eslint',
                    exclude: /node_modules/
                }
            ],
            loaders: [
                { test: /\.vue$/, loader: 'vue' },
                { test: /\.json$/, loader: 'json' },
                { test: /\.html$/, loader: 'html' },
                {
                    test: /\.js$/,
                    loader: 'babel',
                    exclude: /(node_modules|psd\.js\/)/,
                },
                {
                    test: /\.(css|less)$/,
                    // https://github.com/coryhouse/react-slingshot/issues/81
                    // loader: 'style!css?sourceMap!less?sourceMap&relativeUrls=true'
                    loader: ExtractTextPlugin.extract('style', cssLoaders)
                },
                {
                    test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/,
                    loader: 'url',
                    query: {
                        name: '[name].[hash:6].[ext]',
                        limit: BASE64_LIMIT
                    }
                }
            ]
        },

        // plugins
        plugins: plugins,
        postcss: getPostCSSConfig(),
        worker: {
            output: {
                filename: 'worker-[name]-[chunkhash:6].js',
                chunkFilename: 'worker-[name]-[chunkhash:6].js',
            }
        },
        vue: {
            loaders: extractCSS ? {
                less: ExtractTextPlugin.extract(cssLoaders)
            } : null,
            postcss: getPostCSSConfig()
        }
    }, extendConfig);

    // Add webpack progressbar
    if(plugins === config.plugins && process.stdout.isTTY) {
        const entry = config.entry;
        const modules = Object.keys(entry);
        const prefix = `Building ${PATHS.APP_NAME}[${modules}]`;

        plugins.unshift(
            WebpackProgressBar({
                format: `${prefix}: [:bar] :percent (:elapsed seconds)`,
                clear: false,
                width: 40
            })
        );
    }

    return config;
};
